//
//  loader.c
//  Source
//
//  Created by Toj on 6/27/23.
//

#include "loader.h"

static load_return_t parse_machfile(
                                    struct vnode         *vp,
                                    vm_map_t        map,
                                    thread_t        thread,
                                    struct mach_header    *header,
                                    off_t            file_offset,
                                    off_t            macho_size,
                                    int            depth,
                                    int64_t            aslr_offset,
                                    int64_t            dyld_aslr_offset,
                                    load_result_t        *result
                                    )
{
    [...] //此处省略大量初始化与检测
    
    /*
     * Loop through each of the load_commands indicated by the
     * Mach-O header; if an absurd value is provided, we just
     * run off the end of the reserved section by incrementing
     * the offset too far, so we are implicitly fail-safe.
     */
    offset = mach_header_sz;
    ncmds = header->ncmds;
    
    while (ncmds--) {
        /*
         *    Get a pointer to the command.
         */
        lcp = (struct load_command *)(addr + offset);
        //lcp设为当前要解析的cmd的地址
        oldoffset = offset;
        //oldoffset是从macho文件内存开始的地方偏移到当前command的偏移量
        offset += lcp->cmdsize;
        //重新计算offset，再加上当前command的长度，offset的值为文件内存起始地址到下一个command的偏移量
        /*
         * Perform prevalidation of the struct load_command
         * before we attempt to use its contents.  Invalid
         * values are ones which result in an overflow, or
         * which can not possibly be valid commands, or which
         * straddle or exist past the reserved section at the
         * start of the image.
         */
        if (oldoffset > offset ||
            lcp->cmdsize < sizeof(struct load_command) ||
            offset > header->sizeofcmds + mach_header_sz) {
            ret = LOAD_BADMACHO;
            break;
        }
        //做了一个检测，与如何加载进入内存无关
        
        /*
         * Act on struct load_command's for which kernel
         * intervention is required.
         */
        switch(lcp->cmd) {
            case LC_SEGMENT: // 将segment中的数据加载并映射到进程的内存空间去
                [...]
                ret = load_segment(lcp,
                                   header->filetype,
                                   control,
                                   file_offset,
                                   macho_size,
                                   vp,
                                   map,
                                   slide,
                                   result);
                break;
            case LC_SEGMENT_64: // 将segment中的数据加载并映射到进程的内存空间去
                [...]
                ret = load_segment(lcp,
                                   header->filetype,
                                   control,
                                   file_offset,
                                   macho_size,
                                   vp,
                                   map,
                                   slide,
                                   result);
                break;
            case LC_UNIXTHREAD: // 开启一个UNIX线程
                if (pass != 1)
                    break;
                ret = load_unixthread(
                                      (struct thread_command *) lcp,
                                      thread,
                                      slide,
                                      result);
                break;
            case LC_MAIN:
                if (pass != 1)
                    break;
                if (depth != 1)
                    break;
                ret = load_main(
                                (struct entry_point_command *) lcp,
                                thread,
                                slide,
                                result);
                break;
            case LC_LOAD_DYLINKER: // 调用/usr/lib/dyld程序
                if (pass != 3)
                    break;
                if ((depth == 1) && (dlp == 0)) {
                    dlp = (struct dylinker_command *)lcp;
                    dlarchbits = (header->cputype & CPU_ARCH_MASK);
                } else {
                    ret = LOAD_FAILURE;
                }
                break;
            case LC_UUID: // 加载128-bit的唯一ID
                if (pass == 1 && depth == 1) {
                    ret = load_uuid((struct uuid_command *) lcp,
                                    (char *)addr + mach_header_sz + header->sizeofcmds,
                                    result);
                }
                break;
            case LC_CODE_SIGNATURE: // 进行数字签名
                [...]
                ret = load_code_signature(
                                          (struct linkedit_data_command *) lcp,
                                          vp,
                                          file_offset,
                                          macho_size,
                                          header->cputype,
                                          result);
                [...]
                break;
                
            case LC_THREAD: // 开启一个MACH线程，但是不分配栈空间
                [...]
                break;
#if CONFIG_CODE_DECRYPTION
            case LC_ENCRYPTION_INFO:    // 加密二进制文件
            case LC_ENCRYPTION_INFO_64: // 加密二进制文件
                if (pass != 3)
                    break;
                ret = set_code_unprotect(
                                         (struct encryption_info_command *) lcp,
                                         addr, map, slide, vp, file_offset,
                                         header->cputype, header->cpusubtype);
                if (ret != LOAD_SUCCESS) {
                    printf("proc %d: set_code_unprotect() error %d "
                           "for file \"%s\"\n",
                           p->p_pid, ret, vp->v_name);
                    /*
                     * Don't let the app run if it's
                     * encrypted but we failed to set up the
                     * decrypter. If the keys are missing it will
                     * return LOAD_DECRYPTFAIL.
                     */
                    if (ret == LOAD_DECRYPTFAIL) {
                        /* failed to load due to missing FP keys */
                        proc_lock(p);
                        p->p_lflag |= P_LTERM_DECRYPTFAIL;
                        proc_unlock(p);
                    }
                    psignal(p, SIGKILL);
                }
                break;
#endif
            default:
                /* Other commands are ignored by the kernel */
                ret = LOAD_SUCCESS;
                break;
        }
        if (ret != LOAD_SUCCESS)
            break;
    }
    if (ret != LOAD_SUCCESS)
        break;
}
